package com.d3works.apps.account.web;

import com.d3works.apps.account.db.Account;
import com.d3works.apps.account.db.AccountingReport;
import com.d3works.apps.account.db.Move;
import com.d3works.apps.account.db.MoveLine;
import com.d3works.apps.account.db.PaymentMoveLineDistribution;
import com.d3works.apps.account.db.repo.AccountingReportRepository;
import com.d3works.apps.account.exception.AccountExceptionMessage;
import com.d3works.apps.account.service.AccountingReportDas2Service;
import com.d3works.apps.account.service.AccountingReportPrintService;
import com.d3works.apps.account.service.AccountingReportService;
import com.d3works.apps.account.service.AccountingReportToolService;
import com.d3works.apps.account.service.MoveLineExportService;
import com.d3works.apps.base.db.App;
import com.d3works.exception.service.TraceBackService;
import com.d3works.i18n.I18n;
import com.d3works.inject.Beans;
import com.d3works.meta.db.MetaFile;
import com.d3works.meta.schema.actions.ActionView;
import com.d3works.meta.schema.actions.ActionView.ActionViewBuilder;
import com.d3works.rpc.ActionRequest;
import com.d3works.rpc.ActionResponse;
import com.google.common.base.Joiner;
import com.google.inject.Singleton;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Singleton
public class AccountingReportController {

  private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

  public void setFieldsFromReportTypeModelAccountingReport(
      ActionRequest request, ActionResponse response) {
    AccountingReport accountingReport = request.getContext().asType(AccountingReport.class);

    try {
      Map<String, Object> modelAccountingReportMap =
          Beans.get(AccountingReportService.class)
              .getFieldsFromReportTypeModelAccountingReport(accountingReport);
      if (modelAccountingReportMap != null) {
        response.setValues(modelAccountingReportMap);
      }
    } catch (Exception e) {
      TraceBackService.trace(response, e);
    }
  }

  /**
   * @param request
   * @param response
   */
  public void searchMoveLine(ActionRequest request, ActionResponse response) {

    try {
      AccountingReport accountingReport = request.getContext().asType(AccountingReport.class);
      AccountingReportService accountingReportService = Beans.get(AccountingReportService.class);

      accountingReport = Beans.get(AccountingReportRepository.class).find(accountingReport.getId());
      AccountingReportToolService accountingReportToolService =
          Beans.get(AccountingReportToolService.class);

      if (accountingReport.getReportType().getTypeSelect()
          == AccountingReportRepository.REPORT_FEES_DECLARATION_PREPARATORY_PROCESS) {

        AccountingReportDas2Service accountingReportDas2Service =
            Beans.get(AccountingReportDas2Service.class);
        if (accountingReportToolService.isThereAlreadyDraftReportInPeriod(accountingReport)) {
          response.setError(
              I18n.get(
                  "There is already an ongoing accounting report of this type in draft status and covering the same period (potentially created by another user). To continue, please delete one of the reports so that only one remains at the status Draft and for the period and then relaunch the print in that remaining report record."));
          return;
        }

        List<Long> paymentMoveLinedistributionIdList =
            accountingReportDas2Service.getAccountingReportDas2Pieces(accountingReport);
        ActionViewBuilder actionViewBuilder =
            ActionView.define(I18n.get(AccountExceptionMessage.ACCOUNTING_REPORT_3));
        actionViewBuilder.model(PaymentMoveLineDistribution.class.getName());
        actionViewBuilder.add("grid", "payment-move-line-distribution-das2-grid");
        actionViewBuilder.add("form", "payment-move-line-distribution-form");
        String domain = "self.id IN (0)";
        if (CollectionUtils.isNotEmpty(paymentMoveLinedistributionIdList)) {
          domain =
              String.format(
                  "self.id in ( %s )", Joiner.on(",").join(paymentMoveLinedistributionIdList));
        }
        actionViewBuilder.domain(domain);

        response.setReload(true);
        response.setView(actionViewBuilder.map());

      } else {
        String query = accountingReportService.getMoveLineList(accountingReport);
        BigDecimal debitBalance = accountingReportService.getDebitBalance();
        BigDecimal creditBalance = accountingReportService.getCreditBalance();

        response.setValue("totalDebit", debitBalance);
        response.setValue("totalCredit", creditBalance);
        response.setValue("balance", debitBalance.subtract(creditBalance));

        ActionViewBuilder actionViewBuilder =
            ActionView.define(I18n.get(AccountExceptionMessage.ACCOUNTING_REPORT_3));
        actionViewBuilder.model(MoveLine.class.getName());
        actionViewBuilder.add("grid", "move-line-grid");
        actionViewBuilder.add("form", "move-line-form");
        actionViewBuilder.param("search-filters", "move-line-filters");
        actionViewBuilder.domain(query);

        response.setView(actionViewBuilder.map());
      }

    } catch (Exception e) {
      TraceBackService.trace(response, e);
    }
  }

  /**
   * @param request
   * @param response
   */
  public void getAccount(ActionRequest request, ActionResponse response) {

    AccountingReport accountingReport = request.getContext().asType(AccountingReport.class);

    try {
      Account account = Beans.get(AccountingReportService.class).getAccount(accountingReport);
      logger.debug("Account : {}", account);
      response.setValue("account", account);
    } catch (Exception e) {
      TraceBackService.trace(response, e);
    }
  }

  /**
   * @param request
   * @param response
   */
  public void getReload(ActionRequest request, ActionResponse response) {

    response.setReload(true);
  }

  /**
   * @param request
   * @param response
   */
  public void replayExport(ActionRequest request, ActionResponse response) {

    AccountingReport accountingReport = request.getContext().asType(AccountingReport.class);
    accountingReport = Beans.get(AccountingReportRepository.class).find(accountingReport.getId());
    MoveLineExportService moveLineExportService = Beans.get(MoveLineExportService.class);

    try {
      moveLineExportService.replayExportMoveLine(accountingReport);
    } catch (Exception e) {
      TraceBackService.trace(response, e);
    }
  }

  /**
   * @param request
   * @param response
   */
  public void printExportMoveLine(ActionRequest request, ActionResponse response) {

    try {
      AccountingReport accountingReport = request.getContext().asType(AccountingReport.class);
      accountingReport = Beans.get(AccountingReportRepository.class).find(accountingReport.getId());
      AccountingReportService accountingReportService = Beans.get(AccountingReportService.class);

      int typeSelect = accountingReport.getReportType().getTypeSelect();

      if (accountingReport.getExportTypeSelect() == null
          || accountingReport.getExportTypeSelect().isEmpty()
          || typeSelect == 0) {
        response.setInfo(I18n.get(AccountExceptionMessage.ACCOUNTING_REPORT_4));
        response.setReload(true);
        return;
      }

      if (accountingReportService.isThereTooManyLines(accountingReport)) {
        response.setAlert(
            I18n.get(
                "A large number of recording has been fetched in this period. Edition can take a while. Do you want to proceed ?"));
      }

      logger.debug("Type selected : {}", typeSelect);

      if ((typeSelect >= AccountingReportRepository.EXPORT_ADMINISTRATION
          && typeSelect < AccountingReportRepository.REPORT_ANALYTIC_BALANCE)) {
        MetaFile accessFile = accountingReportService.export(accountingReport);

        if ((typeSelect == AccountingReportRepository.EXPORT_ADMINISTRATION
                || typeSelect == AccountingReportRepository.EXPORT_N4DS)
            && accessFile != null) {

          response.setView(
              ActionView.define(I18n.get("Export file"))
                  .model(App.class.getName())
                  .add(
                      "html",
                      "ws/rest/com.d3works.meta.db.MetaFile/"
                          + accessFile.getId()
                          + "/content/download?v="
                          + accessFile.getVersion())
                  .param("download", "true")
                  .map());
        }
      } else {
        if (Beans.get(AccountingReportToolService.class)
                .isThereAlreadyDraftReportInPeriod(accountingReport)
            && accountingReport.getReportType().getTypeSelect()
                == AccountingReportRepository.REPORT_FEES_DECLARATION_PREPARATORY_PROCESS) {
          response.setError(
              I18n.get(
                  "There is already an ongoing accounting report of this type in draft status and covering the same period (potentially created by another user). To continue, please delete one of the reports so that only one remains at the status Draft and for the period and then relaunch the print in that remaining report record."));
          return;
        }
        String fileLink = accountingReportService.print(accountingReport);
        String name = Beans.get(AccountingReportPrintService.class).computeName(accountingReport);
        response.setView(ActionView.define(name).add("html", fileLink).map());
      }
    } catch (Exception e) {
      TraceBackService.trace(response, e);
    }
  }

  public void createExportFromReport(ActionRequest request, ActionResponse response) {

    AccountingReport accountingReport = request.getContext().asType(AccountingReport.class);
    accountingReport = Beans.get(AccountingReportRepository.class).find(accountingReport.getId());

    AccountingReportService accountingReportService = Beans.get(AccountingReportService.class);
    AccountingReportDas2Service accountingReportDas2Service =
        Beans.get(AccountingReportDas2Service.class);

    try {
      AccountingReport accountingExport =
          accountingReportDas2Service.getAssociatedDas2Export(accountingReport);

      if (accountingExport != null) {
        response.setNotify(I18n.get("There is already N4DS export generated for this report."));

      } else {
        boolean complementaryExport = false;

        if (accountingReportDas2Service.isThereAlreadyDas2ExportInPeriod(accountingReport)) {
          complementaryExport = true;
          response.setNotify(
              I18n.get(
                  "There is already N4DS export for this period. The accounting export created will generate complementary N4DS export."));
        }
        accountingExport =
            accountingReportService.createAccountingExportFromReport(
                accountingReport, AccountingReportRepository.EXPORT_N4DS, complementaryExport);
      }
      response.setView(
          ActionView.define(I18n.get(AccountExceptionMessage.ACCOUNTING_REPORT_8))
              .model(AccountingReport.class.getName())
              .add("form", "accounting-report-export-form")
              .add("grid", "accounting-report-export-grid")
              .domain("self.reportType.typeSelect >= 1000 and self.reportType.typeSelect < 2000")
              .param("forceEdit", "true")
              .context("_showRecord", String.valueOf(accountingExport.getId()))
              .map());
    } catch (Exception e) {
      TraceBackService.trace(response, e);
    }
  }

  public void showMoveExported(ActionRequest request, ActionResponse response) {
    AccountingReport accountingReport = request.getContext().asType(AccountingReport.class);

    ActionViewBuilder actionViewBuilder =
        ActionView.define(I18n.get(AccountExceptionMessage.ACCOUNTING_REPORT_6));
    actionViewBuilder.model(Move.class.getName());
    actionViewBuilder.add("grid", "move-grid");
    actionViewBuilder.param("search-filters", "move-filters");
    actionViewBuilder.domain("self.accountingReport.id = :_accountingReportId");
    actionViewBuilder.context("_accountingReportId", accountingReport.getId());

    response.setView(actionViewBuilder.map());
  }
}
